﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazz is open software: you can redistribute it and/or modify it 
// under the terms of the GNU Affero General Public License (AGPL) as published by 
// the Free Software Foundation, version 3 of the License.
// 
// Jazz is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty 
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
//  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public 
// License along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
using System;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TrackerRealm.Jazz.Client;
using PostSharp.Extensibility;
using TrackerRealm.Jazz.Client.Custom;

namespace JazzClientTest
{
    /// <summary>
    /// A test suite that demonstrates advanced State Transistion tables.
    /// This builds on "StateTests"
    /// <para>State transistion tables provided enhanced reliability and
    /// support visual state tables to be generated.</para>
    /// </summary>
    [TestClass]
    public class TransistionTableTests
    {
        #region ApproveWorkflow_StateTable_HappyPath_Verbose
        /// <summary>
        /// This demonstrates a correct path of execution.
        /// That is data is entered, then it is approved.
        /// See the Console output to see the details of the operations
        /// </summary>
        [TestMethod]
        public void ApproveWorkflow_StateTable_HappyPath_Verbose()
        {
            NexusConfig config = new NexusConfig()
            {
                RoleCheckCollection = new string[] { "Manager" },
                Verbose = VerboseType.All,
            };
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                Assert.AreEqual("Guest", nexus.LoginProfile.User.Name);
                Assert.AreEqual(0, nexus.LoginRoles.Count());

                PersonnelRequest myRequest = new PersonnelRequest(nexus);
                //
                // Enter some data - this will change the state the Approval state.
                myRequest.Request = "May I leave early on Friday?";
                //
                // change user
                //
                using(LoggedinUser loginUser = nexus.ChangeLoginProfile(jUser.Create("Joe", "Manager")))
                {
                    Assert.AreEqual("Joe", nexus.LoginProfile.User.Name);
                    Assert.AreEqual(1, nexus.LoginRoles.Count());
                    Assert.IsTrue(nexus.LoginRoles.Contains("Manager"));
                    //
                    // Approve the workflow
                    myRequest.Approve();
                }
            }
        }
        #endregion
        #region UITags
        /// <summary>
        /// UI Tag
        /// </summary>
        [TestMethod]
        public void UITags()
        {
            NexusConfig config = new NexusConfig() { 
                RoleCheckCollection = new string[] { "Manager" },
                Verbose = VerboseType.All};
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                PersonnelRequest myRequest = new PersonnelRequest(nexus);
                jMemberInfo[] members = myRequest.GetMembers().UI().ToWrapper(myRequest).ToArray();
                jMemberInfo approve = members.Where(m => m.Name == "Approve").Single();
                Assert.AreEqual(Tags.ApproveTag, approve.Tag);
            }
        }
        #endregion
        #region StatesCollection
        /// <summary>
        /// StatesCollection
        /// </summary>
        [TestMethod]
        public void StatesCollection()
        {
            NexusConfig config = new NexusConfig()
            {
                RoleCheckCollection = new string[] { "Manager" },
                Verbose = VerboseType.All
            };
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                PersonnelRequest myRequest = new PersonnelRequest(nexus);
                StatesCollection states = myRequest.Class.States;
                Assert.AreEqual(3, states.Count);
                // start state
                {
                    jState state = states.Find("Start");
                    Assert.AreNotEqual(jState.Empty, state);
                    Assert.IsTrue(state.IsStartState);
                    Assert.IsFalse(state.IsFinalState);
                    Assert.AreEqual("", state.Tag);
                }
                // approve state
                {
                    jState state = states.Find("Approval");
                    Assert.AreNotEqual(jState.Empty, state);
                    Assert.IsFalse(state.IsStartState);
                    Assert.IsFalse(state.IsFinalState);
                    Assert.AreEqual(Tags.ApproveStateTag, state.Tag);
                }
                // final state
                {
                    jState state = states.Find("Completed");
                    Assert.AreNotEqual(jState.Empty, state);
                    Assert.IsFalse(state.IsStartState);
                    Assert.IsTrue(state.IsFinalState);
                    Assert.AreEqual("", state.Tag);
                }
            }
        }
        #endregion
        #region StateTableTest
        /// <summary>
        /// StateTableTest
        /// </summary>
        [TestMethod]
        public void StateTableTest()
        {
            NexusConfig config = new NexusConfig()
            {
                RoleCheckCollection = new string[] { "Manager" },
                Verbose = VerboseType.All
            };
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                PersonnelRequest myRequest = new PersonnelRequest(nexus);
                StateTable stateTable = myRequest.Class.StateTable;
                Assert.AreEqual(2, stateTable.Count);
                StateTableEntry[] tableEntries = stateTable.ToArray();
                Assert.IsTrue(stateTable.Where(e => e.Name == "Start-Approval").Count() == 1);
                Assert.IsTrue(stateTable.Where(e => e.Name == "Approval-Completed").Count() == 1);
                //
                // "Start-Approval"
                //
                StateTableEntry start_approval = stateTable.Where(e => e.Name == "Start-Approval").Single();
                Assert.AreEqual(myRequest.Class.States.Find("Start"), start_approval.CurrentState);
                Assert.AreEqual(myRequest.Class.States.Find("Approval"), start_approval.NextState);
                Assert.AreEqual("Request", start_approval.MemberInfo.Name);
                //
                // "Approval-Completed"
                //
                StateTableEntry approval_completed = stateTable.Where(e => e.Name == "Approval-Completed").Single();
                Assert.AreEqual(myRequest.Class.States.Find("Approval"), approval_completed.CurrentState);
                Assert.AreEqual(myRequest.Class.States.Find("Completed"), approval_completed.NextState);
                Assert.AreEqual("Approve", approval_completed.MemberInfo.Name);
                
            }
        }
        #endregion
        #region PersonnelRequest Class - Used in all tests
        /// <summary>
        /// This is a small class that demonstrates the use of states and roles in
        /// a personel request.
        /// The class is very simple only allowing a request to made and
        /// then approved.
        /// </summary>
        [Jazz]
        public class PersonnelRequest : jObject
        {
            #region Create Empty
            /// <summary>
            /// Creates the Empty object for this class ("PersonnelRequest").
            /// </summary>
            /// <param name="nexus"></param>
            /// <returns></returns>
            public static PersonnelRequest CreateEmpty(ClientNexus nexus)
            {
                PersonnelRequest pr = new PersonnelRequest();
                pr.Owner = (jProfile)nexus.LoginProfile.Class.EmptyInstance;
                return pr;
            }
            #endregion
            #region Constructor
            /// <summary>
            /// Constructor
            /// </summary>
            public PersonnelRequest()
                : base()
            {
                this.data = "";
                this.IsApproved = false;
            }
            /// <summary>
            /// Constructor
            /// </summary>
            public PersonnelRequest(ClientNexus nexus)
            :base(nexus)
            {
                this.data = "";
                this.IsApproved = false;
                this.Owner = nexus.LoginProfile;
            }
            #endregion
            #region Owner
            /// <summary>
            /// Returns the 'User' that owns this jObject.
            /// </summary>
            public jProfile Owner { get; private set; }
            #endregion
            #region Request
            private string data;
            /// <summary>
            /// Make a request by setting the data.
            /// </summary>
            [UI]
            public string Request
            {
                get { return this.data; }
                [GrantStates("Start")]
                set
                {
                    this.data = value;
                    // prevent the data from being changed.
                    this.EnterState("Approval");
                }
            }
            #endregion
            #region IsApproved
            /// <summary>
            /// Returns true when the Request has been approved.
            /// </summary>
            [UI]
            public bool IsApproved
            {
                get;
                private set;
            }
            #endregion
            #region Approve/Deny/Edit
            /// <summary>
            /// The approve method is only allowed to be executed when this instance is the 'Approval State'
            /// and the logged in user is "TheBoss"
            /// </summary>
            [UI("Tags.ApproveTag")]
            [GrantStates("Approval")]
            [GrantRoles("Manager")]
            public void Approve()
            {
                this.IsApproved = true;
                // prevent the workflow from being approved again.
                this.EnterState("Completed");
            }
            /// <summary>
            /// The Deny method is only allowed to be executed when this instance is the 'Approval State'
            /// and the logged in user is "TheBoss"
            /// </summary>
            [UI]
            [GrantStates("Approval")]
            [GrantRoles("Manager")]
            public void Deny()
            {
                this.IsApproved = false;
                // prevent the workflow from being approved again.
                this.EnterState("Completed");
            }
            [GrantProperty("Owner")]
            [GrantStates("Approval")]
            public void Edit()
            {
            }
            #endregion
            #region States
            //
            // A ProfilelRequest instance has its 'CurrentState' set to one
            // of the following states.
            //
            #region Start
            [StartState]
            [StateTo(ToState = "Approval", Via = "Request")]
            void Start()
            {
            }
            #endregion
            #region Completed
            [FinalState]
            void Completed()
            {
                Console.WriteLine(this.ToString() + "\t\t***** Completed ******");
            }
            #endregion
            #region Approval
            [StateTo(ToState = "Completed", Via = "Approve")]
            [State("Tags.ApproveStateTag")]
            void Approval()
            {
            }
            #endregion
            #endregion
        }
        #endregion
        #region Tags
        /// <summary>
        /// 
        /// </summary>
        public class Tags
        {
            /// <summary>
            /// static constructor
            /// </summary>
            static Tags()
            {
                ApproveStateTag = new DescriptionTag()
                    {
                        Description = "A manager is able to approve this request.",
                        DisplayName = "Approval State",
                    };
            }
            /// <summary>
            /// Tag as a property. Used in class 'PersonnelRequest' on the state method 'Approval'
            /// </summary>
            public static DescriptionTag ApproveStateTag
            {
                get;
                private set;
            }
            /// <summary>
            /// Tag as field. Used in class 'PersonnelRequest' on the method 'Approve'.
            /// </summary>
            public readonly static DescriptionTag ApproveTag = new DescriptionTag()
            {
                Description = "Approve this request",
                DisplayName = "Approve Request",
            };
            
        }
        public class DescriptionTag
        {
            public string DisplayName { get; set; }
            public string Description { get; set; }
        }
        #endregion
    }
}
